package ai;

import model.ChessAction;

import java.util.HashMap;
import java.util.Map;

/**
 * 单例模式构造普通模式AI
 *
 * @author 任禹名
 */
public class NormalAI implements Ai {
    private static NormalAI instance;

    private NormalAI(ChessAction chessAction) {
        this.chessAction = chessAction;
        this.nowTable = chessAction.getNowTable();
        init();
    }

    public static NormalAI getInstance(ChessAction chessAction) {
        if (instance == null)
            instance = new NormalAI(chessAction);
        return instance;
    }

    private boolean isStarted;
    private int i, j, k, m, n, totalCnt = 0;
    private final int WHITE = 2, BLACK = 1;
    // 白子为1，黑子为2,初始为0；
    private boolean[][][] blackWinTable = new boolean[15][15][572]; // 黑棋获胜组合,572代表黑棋5连子所有出现在棋盘上的情况
    private boolean[][][] whiteWinTable = new boolean[15][15][572]; // 白棋获胜组合
    private int[][] whiteValue = new int[15][15]; // 白棋在棋盘上各个位置的分值
    private int[][] blackValue = new int[15][15]; // 黑棋在棋盘上各个位置的分值
    private int whiteGrade, blackGrade;             //黑白棋子分数
    private int[][] win = new int[3][572]; // 记录棋子在棋盘上的获胜组合中填入了多少棋子，如果在某种可能中填满5个，就获胜
    private int whiteM, whiteN, blackM, blackN;//坐标
    private boolean[][][][] blackOldTable = new boolean[15][15][572][30]; // 旧黑棋获胜组合，四维数组是为了悔棋30次
    private boolean[][][][] whiteOldTable = new boolean[15][15][572][30]; // 旧白棋获胜组合
    private int[][][] oldWhiteValue = new int[15][15][30]; // 旧白棋在棋盘上各个位置的分值，同上为了悔棋30次
    private int[][][] oldBlackValue = new int[15][15][30]; // 旧黑棋在棋盘上各个位置的分值
    private int[][][] oldWin = new int[3][572][30]; // 旧的记录棋子在棋盘上的获胜组合中填入了多少棋子，同上为了悔棋30次
    private int stackCnt = -1;
    private ChessAction chessAction;
    private int[][] nowTable;
    private Map<Integer, Integer> values = new HashMap<Integer, Integer>();

    public void setStarted(boolean started) {
        isStarted = started;
    }

    //算法拿
    @Override
    public int[] downChess(int chessColor) // 找出电脑最佳落子点
    {
        int index[] = new int[2];
        for (i = 0; i <= 14; i++)
            // 遍历棋盘上的所有坐标
            for (j = 0; j <= 14; j++) {
                this.blackValue[i][j] = 0; // 该坐标的黑子奖励积分清零
                if (nowTable[i][j] == 0) // 在还没下棋子的地方遍历
                    for (k = 0; k < 572; k++)
                        // 遍历该棋盘可落子点上的黑子所有权值的连子情况，并给该落子点加上相应奖励分
                        if (this.blackWinTable[i][j][k]) {
                            this.blackValue[i][j] += values.get(this.win[2][k]);
                            if (chessColor == BLACK) {
                                if (this.win[2][k] == 2) {
                                    this.blackValue[i][j] += 2;
                                } else if (this.win[2][k] > 2) {
                                    this.blackValue[i][j] += 10;
                                }
                            }
                        }
                this.whiteValue[i][j] = 0;// 该坐标的白子的奖励积分清零
                if (nowTable[i][j] == 0) // 在还没下棋子的地方遍历
                    for (k = 0; k < 572; k++)
                        // 遍历该棋盘可落子点上的白子所有权值的连子情况，并给该落子点加上相应奖励分
                        if (this.whiteWinTable[i][j][k]) {
                            this.whiteValue[i][j] += values.get(this.win[2][k]);
                            if (chessColor == WHITE) {
                                if (this.win[2][k] == 2) {
                                    this.whiteValue[i][j] += 2;
                                } else if (this.win[2][k] > 2) {
                                    this.whiteValue[i][j] += 10;
                                }
                            }
                        }
            }
        if (this.isStarted) { // 开始时白子落子坐标
            if (nowTable[7][7] == 0) {
                m = 7;
                n = 7;
            } else {
                m = 7;
                n = 8;
            }
            this.isStarted = false;
        } else {
            for (i = 0; i < 15; i++) {
                for (j = 0; j < 15; j++) {
                    if (nowTable[i][j] == 0) { // 找出棋盘上可落子点的黑子白子的各自最大权值，找出各自的最佳落子点
                        if (this.whiteValue[i][j] >= this.whiteGrade) {
                            this.whiteGrade = this.whiteValue[i][j];
                            this.whiteM = i;
                            this.whiteN = j;
                        }
                        if (this.blackValue[i][j] >= this.blackGrade) {
                            this.blackGrade = this.blackValue[i][j];
                            this.blackM = i;
                            this.blackN = j;
                        }
                    }
                }
            }
            if (this.whiteGrade >= this.blackGrade) { // 如果白子的最佳落子点的权值比黑子的最佳落子点权值大，则电脑的最佳落子点为白子的最佳落子点，否则相反
                m = whiteM;
                n = whiteN;
            } else {
                m = blackM;
                n = blackN;
            }
        }
        this.whiteGrade = 0;
        this.blackGrade = 0;
        // 电脑下子位置
        System.out.println("电脑下在了" + m + "," + n);
        for (i = 0; i < 572; i++) {
            if (this.whiteWinTable[m][n][i] && this.win[1][i] != 7) {
                this.win[1][i]++; // 给白子的所有五连子可能的加载当前连子数
            }
            if (this.blackWinTable[m][n][i]) {
                this.blackWinTable[m][n][i] = false;
                this.win[2][i] = 7;//7无意义，只要大于5就行，代表572中的第i种被另外一个棋子所占，不可能以这种方式获胜
            }
        }

        index[0] = m;
        index[1] = n;

        return index;
    }

    @Override
    public void delete(int x, int y) {
        // TODO Auto-generated method stub
        for (int i = 0; i < 15; i++) {
            for (int j = 0; j < 15; j++) {
                for (int z = 0; z < 572; z++) {
                    blackWinTable[i][j][z] = blackOldTable[i][j][z][stackCnt];
                    whiteWinTable[i][j][z] = whiteOldTable[i][j][z][stackCnt];
                }
            }
        }


        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 572; j++) {
                win[i][j] = oldWin[i][j][stackCnt];
            }
        }


        for (int i = 0; i < 15; i++) {
            for (int j = 0; j < 15; j++) {
                whiteValue[i][j] = oldWhiteValue[i][j][stackCnt];
                blackValue[i][j] = oldBlackValue[i][j][stackCnt];
            }
        }

        this.stackCnt--;
    }

    @Override
    public void reset(ChessAction chessAction) {
        this.chessAction = chessAction;
        this.nowTable = chessAction.getNowTable();
        this.stackCnt = -1;
        this.isStarted = true;
        totalCnt = 0;
        // 初始化棋盘
        for (i = 0; i < 15; i++) {
            for (j = 0; j < 15; j++) {
                this.blackValue[i][j] = 0;
                this.whiteValue[i][j] = 0;
            }
        }
        // 遍历所有的五连子可能情况的权值

        // 横
        for (i = 0; i < 15; i++) {
            for (j = 0; j < 11; j++) {
                for (k = 0; k < 5; k++) {
                    this.blackWinTable[j + k][i][totalCnt] = true;
                    this.whiteWinTable[j + k][i][totalCnt] = true;
                }
                totalCnt++;
            }
        }


        // 竖
        for (i = 0; i < 15; i++) {
            for (j = 0; j < 11; j++) {
                for (k = 0; k < 5; k++) {
                    this.blackWinTable[i][j + k][totalCnt] = true;
                    this.whiteWinTable[i][j + k][totalCnt] = true;
                }
                totalCnt++;
            }
        }


        // 捺
        for (i = 0; i < 11; i++)
            for (j = 0; j < 11; j++) {
                for (k = 0; k < 5; k++) {
                    this.blackWinTable[j + k][i + k][totalCnt] = true;
                    this.whiteWinTable[j + k][i + k][totalCnt] = true;
                }
                totalCnt++;
            }

        // 撇
        for (i = 0; i < 11; i++) {
            for (j = 14; j >= 4; j--) {
                for (k = 0; k < 5; k++) {
                    this.blackWinTable[j - k][i + k][totalCnt] = true;
                    this.whiteWinTable[j - k][i + k][totalCnt] = true;
                }
                totalCnt++;
            }
        }


        for (i = 1; i <= 2; i++) {
            // 初始化黑子白子上的每个权值上的连子数
            for (j = 0; j < 572; j++) {
                this.win[i][j] = 0;
            }
        }
    }

    /**
     * 初始化分值表.
     */
    public void init() {
        values.put(1, 5);
        values.put(2, 50);
        values.put(3, 140);
        values.put(4, 400);
    }

    /**
     * 在下棋前保留棋局，并计算连子数.
     *
     * @param x 上次人类下棋的坐标
     * @param y 上次人类下棋的坐标
     */
    public void initBeforeDownChess(int x, int y) {
        stackCnt++;
        //保存棋局
        for (int i = 0; i < 15; i++) {
            for (int j = 0; j < 15; j++) {
                for (int z = 0; z < 572; z++) {
                    blackOldTable[i][j][z][stackCnt] = blackWinTable[i][j][z];
                    whiteOldTable[i][j][z][stackCnt] = whiteWinTable[i][j][z];
                }
            }
        }


        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 572; j++)
                oldWin[i][j][stackCnt] = win[i][j];


        for (int i = 0; i < 15; i++)
            for (int j = 0; j < 15; j++) {
                oldWhiteValue[i][j][stackCnt] = whiteValue[i][j];
                oldBlackValue[i][j][stackCnt] = blackValue[i][j];
            }


        for (i = 0; i < 572; i++) {
            if (this.blackWinTable[x][y][i] && this.win[2][i] != 7)
                this.win[2][i]++;//给黑子的所有五连子可能的加载当前连子数
            if (this.whiteWinTable[x][y][i]) {
                this.whiteWinTable[x][y][i] = false;
                this.win[1][i] = 7;
            }
        }
    }


}
